use std::{fs, io};
use std::fs::Permissions;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;

use crate::runtime::runtime::error::InternalError;
use crate::runtime::runtime::result::InternalErrorResult;

/// 提权
pub fn chmod<P: AsRef<Path>>(path: P) -> InternalErrorResult<()> {
    let path = path.as_ref();
    if !path.exists() {
        return Ok(());
    }

    let result = fs::set_permissions(path, Permissions::from_mode(0o777));
    if result.is_err() {
        return Err(InternalError::new(format!("Could not chmod: {} {}", path.to_str().unwrap(), result.unwrap_err())));
    }

    if path.is_dir() {
        let entries = fs::read_dir(path);
        if entries.is_err() {
            return Err(InternalError::new(format!("Could read dir: {} {}", path.to_str().unwrap(), result.unwrap_err())));
        }

        let entries = entries.unwrap();
        for entry in entries {
            if entry.is_err() {
                return Err(InternalError::new(format!("Could read dir: {} {}", path.to_str().unwrap(), result.unwrap_err())));
            }

            let entry = entry.unwrap();
            chmod(entry.path().to_str().unwrap())?;
        }
    }

    Ok(())
}

/// 封装系统的remove_dir_all
/// 路径不存在时不返回错误
pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> InternalErrorResult<()> {
    let path = path.as_ref();
    if !path.exists() {
        return Ok(());
    }

    chmod(path)?;

    let result = fs::remove_dir_all(path);
    if result.is_err() {
        let error = result.unwrap_err();
        if io::ErrorKind::NotFound != error.kind() {
            return Err(InternalError::new(format!("Could not remove target path: {} {}", path.to_str().unwrap(), error)));
        };
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use tempfile;

    #[test]
    fn test_chmod() {
        let result = super::chmod("/path/to/not exists");
        assert_eq!(true, result.is_ok());

        let result = super::chmod(tempfile::tempdir().unwrap().path().to_str().unwrap());
        assert_eq!(true, result.is_ok());
    }

    #[test]
    fn test_remove_dir_all() {
        let result = super::remove_dir_all(tempfile::tempdir().unwrap().path().to_str().unwrap());
        assert_eq!(true, result.is_ok());

        let result = super::remove_dir_all("/path/to/not exists");
        assert_eq!(true, result.is_ok());
    }
}